package httpproxy.client;

import httpproxy.HttpProxyMain;
import httpproxy.server.DeniedItem;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

public class HttpResponse
{
	private static int MAX_LENGTH = 5120;
	
	private int contentLength = 0;
	private String contentType = "";
	private byte[] contentData = null;
	private String contentLanguage = "";
	private String contentLocation = "";
	private String contentEncoding = "";
	private String contentScriptType = "";
	private String contentStyleType = "";
	
	private String xPoweredBy = "";
	
	private String server = "";
	private String connection = "";
	
	private String warning = "";
	private String via = "";
	
	private String pragma = "";
	
	private String transferEncoding="";
	private String expires = "";
	
	private String proxyAuthenticate = "";
	private String etag = "";
	
	private String wwwAuthenticate = "";
	private String xcache = "";
	
	private List<String> headers = new ArrayList<String>();
	private List<String> setCokies = new ArrayList<String>();
	
	private File cachedFile = null;
	private String version = "";
	private int statusCode;
	private String statusMessage = "";
	private String cacheControl = "";
	private String acceptRanges = "";
	private String lastModified = "";
	private String date = "";
	private boolean denied = false;
	private String fromHost = "";
	
	public boolean isDenied(){
		return denied;
	}
	
	public HttpResponse(String id)
	{
		cachedFile = new File(id);
	}
	
	public HttpResponse(String id, String host)
	{
		cachedFile = new File(id);
		fromHost = host;
	}
	
	public void setFromHost(String h)
	{
		fromHost = h;
	}

	public int getContentLength() {
		return contentLength;
	}

	public void setContentLength(int contentLength) {
		this.contentLength = contentLength;
	}

	public String getContentType() {
		return contentType;
	}

	public void setContentType(String contentType) {
		this.contentType = contentType;
	}
	
	public String getConnection() {
		return connection;
	}

	public void setConnection(String con) {
		this.connection = con;
	}

	public String getServer() {
		return server;
	}

	public void setServer(String srv) {
		this.server = srv;
	}
	
	public byte[] getContentData()
	{
		return contentData;
	}
	
	public void readResponseHeader(InputStream inStream)
	{
		int len = 0;
	    byte[] oneByte = new byte[1];
	    byte[] temp, buff = new byte[1024];
	    
	    /*
	    * Read by lines
	    */
	    String[] lines = null;
	    String s = "";
	    try {
	       while(inStream.read(oneByte) > -1)
	       {
	    	   if(len == buff.length)
	    	   {
	    		   temp = buff;
	    		   buff = new byte[buff.length + 512];
	    		   for(int i=0;i<len;i++)
	    			   buff[i] = temp[i];
	    	   }
	    	   
	    	   buff[len++] = oneByte[0];
	    	   if(len>=4)
	    		   s = new String(buff, len-4, 4);
	    		   
	    	   if( len>=4 && s.contains("\r\n\r\n") )
	    	   {
	    		   String headers = new String(buff, 0, len);
	    		   //System.out.println("************RESPONSE HEADER FROM SERVER**************");
	    		   //System.out.print(headers);
	    		   //System.out.println("\n************RESPONSE HEADER FROM SERVER**************");
	    		   lines = ((headers.split("\r\n\r\n"))[0]).split("\r\n");
	    		   break;
	    	   }
	       }//end while
	       
	       if(lines != null) 
	       {
		    	//System.out.println("************** RESPONSE HEADER AFTER PARSING ***********");
		    	String[] status = lines[0].split("\\s"); 
		    	version = status[0];
		    	statusCode = Integer.parseInt(status[1].trim());
		    	statusMessage = status[2];
		    	
		    	for(String line:lines)
		    	{
		    		extractResponseHeader(line);
		    		//System.out.print(line + "\r\n");
		    	}
		    	//System.out.println("\r\n**************RESPONSE HEADER AFTER PARSING***************");
	       }
	    } catch (IOException e) {
	    	e.printStackTrace();
	    }
	}
	
	public void sendResponseHeader(OutputStream outStream) throws IOException
	{
		String strHeader = toString(false);
		DataOutputStream dout = new DataOutputStream(outStream);
		dout.writeBytes(strHeader);
		System.out.println("[BEGIN]\r\n" + strHeader + "[END]");
    	dout.flush();
	}
	
	public void extractResponseHeader(String line)
	{		
		if(line.trim().startsWith("Server") )
		{
			server = (line.replace("Server", "")).replace(":", "").trim();
		}
					
		if(line.trim().startsWith("Content-Type") )
		{
			contentType = (line.replace("Content-Type", "")).replace(":", "").trim();
		}
		
		if(line.trim().startsWith("Content-Encoding") )
		{
			contentEncoding = (line.replace("Content-Encoding", "")).replace(":", "").trim();
		}
			
		if(line.trim().startsWith("Connection") )
		{
			connection = (line.replace("Connection", "")).replace(":", "").trim();
		}
		
		if(line.trim().toUpperCase().startsWith("CONTENT-LENGTH") )
		{
			contentLength = Integer.parseInt((line.replace("Content-Length", "")).replace(":", "").trim());
		}
		
		if(line.trim().startsWith("Content-Language") )
		{
			contentLanguage = line.replace("Content-Language", "").replace(":", "").trim();
		}
		
		if(line.trim().startsWith("Content-Script-Type") )
		{
			contentScriptType = line.replace("Content-Script-Type", "").replace(":", "").trim();
		}
		
		if(line.trim().startsWith("Content-Style-Type") )
		{
			contentStyleType = line.replace("Content-Style-Type", "").replace(":", "").trim();
		}
		
		if(line.trim().startsWith("Transfer-Encoding") )
		{
			transferEncoding = line.replace("Transfer-Encoding", "").replace(":", "").trim();
		}
		
		if(line.trim().toUpperCase().startsWith("EXPIRES") )
		{
			expires = line.replace("Expires", "").replace(":", "").trim();
		}
		
		
		if(line.trim().startsWith("Accept-Ranges") )
		{
			acceptRanges = line.replace("Accept-Ranges", "").replace(":", "").trim();
		}
		
		if(line.trim().startsWith("Cache-Control") )
		{
			cacheControl = line.replace("Cache-Control", "").replace(":", "").trim();
		}
		
		if(line.trim().startsWith("WWWW-Authenticate") )
		{
			wwwAuthenticate = line.replace("WWWW-Authenticate", "").replace(":", "").trim();
		}
		
		if(line.trim().startsWith("Proxy-Authenticate") )
		{
			proxyAuthenticate = line.replace("Proxy-Authenticate", "").replace(":", "").trim();
		}
		
		if(line.trim().toUpperCase().startsWith("SET-COOKIE") )
		{
			setCokies.add(line.replace("Set-Cookie", "").replace(":", "").trim());
		}
		
		if(line.trim().toUpperCase().startsWith("X-CACHE") )
		{
			xcache = line.replace("X-Cache", "").replace(":", "").trim();
		}
		
		if(line.trim().toUpperCase().startsWith("DATE") )
		{
			date = line.replace("Date", "").replace(":", "").trim();
		}
		
		if(line.trim().toLowerCase().startsWith("last-modified") )
		{
			lastModified = line.replace("Last-Modified", "").replace(":", "").trim();
		}
		
		if(line.trim().toUpperCase().startsWith("PRAGMA") )
		{
			pragma = line.replace("Pragma", "").replace(":", "").trim();
		}
		
		if(line.trim().toUpperCase().startsWith("ETAG") )
		{
			etag = line.replace("ETag", "").replace(":", "").trim();
		}
		
		if(line.trim().toUpperCase().startsWith("VIA") )
		{
			via = line.replace("Via", "").replace(":", "").trim();
		}
		
		if(line.trim().toUpperCase().startsWith("X-POWERED-BY") )
		{
			xPoweredBy = line.replace("X-Powered-By", "").replace(":", "").trim();
		}
		
		
		
		headers.add(line);
	}
	
	private String toString(boolean isSend)
	{
		String CR = "\r\n";
		String headers = version + " " + statusCode + " " + statusMessage + CR;
		
		
		
		if(server.length()>0)
			headers += String.format("Server: %s\r\n", server);
		
		if(contentType.length()>0)
		{
			System.out.println("[HttpResponse] " + fromHost + " " + contentType );
			for(DeniedItem itm:HttpProxyMain.getACL())
			{
				if(itm.server.equals(fromHost) && contentType.contains(itm.type))
				{
					denied = true;
					
					if(contentType.toUpperCase().contains("IMAGE"))
					{	
						contentData = HttpProxyMain.getAltImage();
						contentType = "image/png";
						contentLength = contentData.length;
					}
					
					if(contentType.toUpperCase().contains("TEXT"))
					{
						contentType = "text/html";
						String msg = "<h3>Access denied from proxy!</h3>";
						contentData = msg.getBytes();
						contentLength = contentData.length;
					}
				}
			}
			
			if(contentLength > 0)
			{
				headers += String.format("Content-Length: %d\r\n", contentLength);
			}
			headers += String.format("Content-Type: %s\r\n", contentType);
		}
		
		if(contentLanguage.length()>0)
			headers += String.format("Content-Language: %s\r\n", contentLanguage);
		
		if(contentLocation.length()>0)
			headers += String.format("Content-Location: %s\r\n", contentLocation);
		
		if(contentEncoding.length()>0)
			headers += String.format("Content-Encoding: %s\r\n", contentEncoding);
		
		if(contentScriptType.length()>0)
			headers += String.format("Content-Script-Type: %s\r\n", contentScriptType);
		if(contentStyleType.length()>0)
			headers += String.format("Content-Style-Type: %s\r\n", contentStyleType);
		
		if(cacheControl.length()>0)
			headers += String.format("Cache-Control: %s\r\n", cacheControl);
		
		if(acceptRanges.length()>0)
			headers += String.format("Accept-Ranges: %s\r\n", acceptRanges);
		
		if(wwwAuthenticate.length()>0)
			headers += String.format("WWWW-Authenticate: %s\r\n", wwwAuthenticate);
		
		if(proxyAuthenticate.length()>0)
			headers += String.format("Proxy-Authenticate: %s\r\n", proxyAuthenticate);
		
		if(via.length() > 0)
			headers += String.format("Via: %s\r\n", via);
		
		if(transferEncoding.length() > 0)
			headers += String.format("Transfer-Encoding: %s\r\n", transferEncoding);
		
		if(expires.length() > 0)
			headers += String.format("Expires: %s\r\n", expires);
		
		for(String cookie:setCokies)
		{
			headers += String.format("Set-Cookie: %s\r\n", cookie);
		}
		
		if(xPoweredBy.length() > 0)
			headers += String.format("X-Powered: %s\r\n", xPoweredBy);
		
		if(lastModified.length()>0)
			headers += String.format("Last-Modified: %s\r\n", lastModified);
		
		if(connection.length()>0)
			headers += String.format("Connection: %s\r\n", connection);
		
		if(etag.length()>0)
			headers += String.format("ETag: %s\r\n", etag);
		
		if(xcache.length()>0)
			headers += String.format("X-Cache: %s\r\n", xcache);
		
		if(date.length()>0)
			headers += String.format("Date: %s\r\n", date);
		
		if(pragma.length()>0)
			headers += String.format("Pragma: %s\r\n", pragma);
		
		headers += "\r\n";
		return headers;
	}
	
	public void readContent(InputStream inStream) throws IOException
	{
		byte[] buff = new byte[1024];
		FileOutputStream cachedStream = null;
		int count, byteread = 0;
		
		//System.out.println("[BEGIN]\r\nContent-Length " + this.contentLength + "[END]");
		
		if(contentLength > MAX_LENGTH)
		{
			cachedStream = new FileOutputStream(cachedFile);
			while (-1 < ( count  = inStream.read(buff))) 
			{
				cachedStream.write(buff,0,count);
				cachedStream.flush();
				byteread += count;
				if(byteread == contentLength)
					break;
			}
			cachedStream.close();
			return;
		}
		
		if(contentLength > 0)
		{
			contentData = new byte[contentLength];
			while (-1 < ( count  = inStream.read(contentData))) 
			{
				//write to content data
				byteread += count;
				if(byteread == contentLength)
					break;
			}
		}
	}
	
	public void sendContentData(OutputStream outStream) throws IOException
	{
		if(contentLength <=0 )
			return;
		
		if(contentData != null)
		{
			outStream.write(contentData, 0, contentLength);
			outStream.flush();
			return;
		}
		
		int count, byteread = 0;
		byte[] buff = new byte[1024];
		FileInputStream cachedStream = new FileInputStream(cachedFile);
		while (-1 < ( count  = cachedStream.read(buff))) 
		{
			outStream.write(buff,0,count);
			outStream.flush();
			byteread += count;
			
			if(byteread == contentLength)
				break;
		}
		cachedStream.close();
		cachedFile.delete();
	}
	
}
